import { Injectable, Inject, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { ConfigType } from '@nestjs/config';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { SignInDto } from './dto/sign-in';
import { SignUpDto } from './dto/sign-up';
import jwtConfigs from 'src/config/jwt.config';
import { HashingService } from './hashingService';
import { ISignData } from 'src/interface/user.interface';
import { User } from '../admin/user/entities/user.entity';

@Injectable()
export class AuthorizationServiceUser {
  constructor(
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
    private readonly jwtService: JwtService,
    @Inject(jwtConfigs.KEY)
    private readonly jwtConfiguration: ConfigType<typeof jwtConfigs>,
    private readonly hashingService: HashingService,
  ) {}

  // 用户注册
  async userSignUp(signUpDto: SignUpDto) {
    const { name, password } = signUpDto;

    const existingUser = await this.userRepository.findOne({
      where: [{ name }],
    });

    if (existingUser) {
      throw new UnauthorizedException('User already exists');
    }

    const hashedPassword = await this.hashingService.hash(password);

    const user = this.userRepository.create({
      ...signUpDto,
      password: hashedPassword,
    });
    return await this.userRepository.save(user);
  }

  // 用户登陆
  async userSignIn(signInDto: SignInDto) {
    const { name, password } = signInDto;

    const user = await this.userRepository
      .createQueryBuilder('user')
      .where({ name })
      .addSelect('user.password')
      .getOne();

    if (!user) {
      throw new UnauthorizedException('User not found');
    }

    const isEqual = await this.hashingService.compare(password, user.password);

    if (!isEqual) {
      throw new UnauthorizedException('password is incorrect');
    }

    return await this.generateTokens(user);
  }

  async generateTokens(instance: User) {
    const token = await this.signToken<Partial<ISignData>>(instance.id, {
      name: instance.name,
      role: instance.role,
      province: instance.province,
      city: instance.city,
    });
    return { token };
  }

  private async signToken<T>(managerId: number, payload?: T) {
    return await this.jwtService.signAsync(
      {
        sub: managerId,
        ...payload,
      },
      {
        secret: this.jwtConfiguration.secret,
        audience: this.jwtConfiguration.audience,
        issuer: this.jwtConfiguration.issuer,
        expiresIn: this.jwtConfiguration.accessTokenTtl,
      },
    );
  }
}
